import entity
import pygame
import operator
import math
import helpers
import xml.etree.cElementTree as ET
from constants import DEBUG, MIN_TURRET_ENEMY_ANGLE
from towers.beam import Beam
from towers.projectile import Projectile

class Tower(entity.Entity):

    ship_list = list()
    module_list = [None, None, None, None]
    player = None
    level = 0
    currentAngle = 0
    
    state = None # Build, Ready
    build_timer = 0
    animation_timer = 0
    build_time = 5
    ANIMATION_INTERVAL = 0.5 # milliseconds
    
    cooldown = 0.0
    kill_count = 0
    
    # Used by canon
    projectile_color = (255,255,0)
    
    health = 20
    max_health = 20
    
    def __init__(self, tower_name, player):
        pygame.sprite.Sprite.__init__(self)
        
        self.load_tower_data(tower_name)
        
        self.player = player
        self.projectile_list = pygame.sprite.Group()
        
        pygame.sprite.Sprite.__init__(self)
        self.base_image, self.base_rect = helpers.load_image(self.base_image_name, -1)
         
        self.image, self.rect = helpers.load_image(self.turret_image_name, -1)
        self.orig_image = self.image        
        
        self.state = 'Build'
        self.sound = helpers.load_sound_fx('laser_fire.wav')

    def find_enemies_in_range(self):
        target_list = pygame.sprite.Group()
        for ship in self.ship_list:
            if pygame.sprite.collide_circle(self, ship):
                target_list.add(ship)
        return target_list
    
    def find_enemy_first(self):
        target_list = self.find_enemies_in_range()
        for ship in sorted(target_list.sprites(),key=operator.attrgetter('distance'), reverse=True):
            return ship
    
    def find_enemy_last(self):
        target_list = self.find_enemies_in_range()
        for ship in sorted(target_list.sprites(),key=operator.attrgetter('distance'), reverse=False):
            return ship
    
    def find_enemy_closest(self):
        pass

    def find_enemy_strongest(self):
        pass
    
    def find_enemy_weakest(self):
        pass

    def find_enemy_fastest(self):
        pass
    
    def find_enemy_slowest(self):
        pass
    
    def get_target(self):
        """
        Return a valid target based on target method.
        """
        return self.find_enemy_first()
    
    def sell(self):
        cost = 0
        for i in range(self.usr_level+1):
            cost += self.baseCost[i-1]
        self.player.money += cost*0.75

    def update(self, ship_list, time):

        # Check if we are done building the tower
        if self.state == 'Build':
            self.build_timer += time
            if self.build_timer > self.build_time:
                self.state = 'Ready'
                return
        
        """Update tower stats"""
        self.ship_list = ship_list
        for projectile in self.projectile_list:
            projectile.update()

        # Reduce the towers cooldown
        self.cooldown -= 1
        # Check if we have a new target
        if self.cooldown <= 0:
            self.target = None
            for ship in sorted(ship_list.sprites(),key=operator.attrgetter('distance'), reverse=True):
                if pygame.sprite.collide_circle(self, ship):
                    self.target = ship
                    # Check if our turret is rotated in the correct direction
                    x = self.rect.centerx - self.target.rect.centerx
                    y = self.rect.centery - self.target.rect.centery
                    angle = math.atan2(x, y)
                    diff = math.fabs( helpers.angleDifference( angle, self.currentAngle ) )                    
                    if ( diff <= MIN_TURRET_ENEMY_ANGLE
                        and self.player.mass_current >= self.shot_mass
                        and self.player.energy_current >= self.shot_energy ):
                        self.projectile_list.add(self.get_projectile())
                        self.cooldown = self.fire_rate
                        self.sound.play()
                        self.player.mass_current -= self.shot_mass
                        self.player.energy_current -= self.shot_energy
                        break
        
    def upgrade(self):
        '''
        Upgrades the tower to the next user level
        '''
        if self.level < 4 and self.player.money >= self.cost:
            self.level += 1
            self.radius = self.baseRadius[self.usr_level + self.exp_level]
            self.damage = self.baseDamage[self.usr_level + self.exp_level]
            self.fire_rate = self.baseFireRate[self.usr_level + self.exp_level]
            self.cost = self.baseCost[self.usr_level]
            self.player.money -= self.cost
            self.state = 'upgrading'
        
    def set_rect_center(self, center):
        '''
        Set new center for the tower rectangle
        '''
        self.position = center
        self.rect.center = center
        self.base_rect.center = center
    
    def draw(self, view_port, time):
        '''
        Draw the tower to the screen. Calls sub draw functions.
        '''
        self.base_rect.centerx = self.position[0] + view_port.topleft[0]
        self.base_rect.centery = self.position[1] + view_port.topleft[1]
        self.rect.centerx = self.position[0] + view_port.topleft[0]
        self.rect.centery = self.position[1] + view_port.topleft[1]        
        
        if self.state == 'Build':
            self.draw_build(view_port, time)
        elif self.state == 'Ready':
            self.draw_ready(view_port)
            
        self.draw_healthbar(view_port)    
            
        if DEBUG:
            if self.target:
                pygame.draw.line(view_port.screen,
                    (0,0,255), (self.rect.centerx, self.rect.centery),
                    (self.target.rect.centerx, self.target.rect.centery), 1 )            
    
    def draw_build(self, view_port, time):
        '''
        Draw function when the tower is in the build state.
        '''
        view_port.screen.blit(self.base_image, self.base_rect)
        self.animation_timer += time
        if self.animation_timer > self.ANIMATION_INTERVAL:
            view_port.screen.blit(self.image, self.rect)
        if self.animation_timer > self.ANIMATION_INTERVAL*2:
            self.animation_timer = 0
        
    def draw_ready(self, view_port):
        '''
        Draw function when the tower is in the ready state.
        '''
        for projectile in self.projectile_list:
            projectile.draw(view_port.screen)

        if self.target:
            center = self.rect.center
            x = self.rect.centerx - self.target.rect.centerx
            y = self.rect.centery - self.target.rect.centery
            angle = math.atan2(x, y)
            diff = helpers.angleDifference( angle, self.currentAngle )
            if diff < 0:
                self.currentAngle = self.currentAngle + min( self.turn_rate, math.fabs(diff) )
            elif diff > 0:
                self.currentAngle = self.currentAngle - min( self.turn_rate, diff )
            self.image = pygame.transform.rotate(self.orig_image, math.degrees(self.currentAngle) )
            self.rect = self.image.get_rect()                    
            self.rect.center = center    
    
        view_port.screen.blit(self.base_image, self.base_rect)
        view_port.screen.blit(self.image, self.rect)      
        
    def add_kill(self, ship):
        '''
        Update stats when a new kill is caused by the tower.
        '''
        self.player.money += ship.reward
        self.killCount += 1
        ship.kill()
            
    def add_module(self, module):
        '''
        Adds a new module to the tower, and upgrades it to the next level.
        '''
        self.module_list[ self.level ] = module
        module.modify_tower( self )
        
    def get_projectile(self):
        '''
        Used by Laser and Canon tower
        '''
        if self.type == "Light" or self.type == "Heavy":
            return Beam(self, self.target, 10)
        elif self.type == "Medium":        
            return Projectile(self, self.target, 10)
        else:
            print("ERROR: Unknown weapon type in Tower()")
        
    def on_hit(self, target):
        if self.type == "Light" or self.type == "Heavy":
            self.on_hit_laser(target)
        elif self.type == "Medium":
            self.on_hit_canon(target)
        else:
            print("ERROR: Unknown on hit type in Tower()")
        
    def on_hit_laser(self, target):
        '''
        Used by Laser tower
        '''
        target.health -= self.damage
        if target.health <= 0:
            target.kill()
            self.kill_count += 1        
        
    def on_hit_canon(self, target):
        """Triggered when a projectile hits a target"""
        target.health -= self.damage
        if target.health <= 0:
            #self.player.money += target.reward
            target.kill()
            self.kill_count += 1
        # AOE Code
        for aoe_target in self.ship_list:
            distance = math.sqrt((target.rect.centerx - aoe_target.rect.centerx)**2 + \
                      (target.rect.centery - aoe_target.rect.centery)**2)
            if distance <= 50 and not distance == 0:
                aoe_target.health -= self.damage
                if aoe_target.health <= 0:
                    #self.player.money += target.reward
                    aoe_target.kill()
                    self.kill_count += 1        
        
    def load_tower_data(self, tower_name):
        '''
        Loads the tower data from the corresponding XML-file
        '''
        tower_name = "../data/towers/" + tower_name + ".xml"
        tree = ET.ElementTree(file=tower_name)
        
        self.name = tree.find('name').text
        self.type = tree.find('type').text
        self.description = tree.find('description').text
        self.damage = float( tree.find('base/damage').text )
        self.radius = float( tree.find('base/radius').text )
        self.fire_rate = float( tree.find('base/firerate').text )
        self.turn_rate = float( tree.find('base/turnrate').text )
        self.turret_image_name = tree.find('turretimage').text
        self.base_image_name = tree.find('baseimage').text
        self.mass_cost = float( tree.find('base/buildcost/mass').text ) 
        self.energy_cost = float( tree.find('base/buildcost/energy').text )
        self.shot_mass = float( tree.find('base/firecost/mass').text ) 
        self.shot_energy = float( tree.find('base/firecost/energy').text )        